﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using MathNet.Numerics.Distributions;

namespace TSTreeThesis.Utilities
{
    public sealed class Utility
    {
        public static int COMPRESSION_RATE = int.Parse(ConfigurationManager.AppSettings["COMPRESSION_RATE"]);
        public static double THRESHOLD_ERROR = double.Parse(ConfigurationManager.AppSettings["THRESHOLD_ERROR"]);
        public static int SLIDING_WINDOW_SIZE_ORIGINAL = int.Parse(ConfigurationManager.AppSettings["SLIDING_WINDOW_SIZE_ORIGINAL"]);
        public static int SLIDING_WINDOW_SIZE = SLIDING_WINDOW_SIZE_ORIGINAL / COMPRESSION_RATE;
        public static int DATA_LENGHT = int.Parse(ConfigurationManager.AppSettings["DATA_LENGHT"]);
        public static int QUANTIZATION = int.Parse(ConfigurationManager.AppSettings["QUANTIZATION"]);
        public static int MAXVALUE = int.Parse(ConfigurationManager.AppSettings["MAXVALUE"]);
        public static int MINVALUE = int.Parse(ConfigurationManager.AppSettings["MINVALUE"]);
        public static int SYMBOLS_LENGTH_DEFAULT = int.Parse(ConfigurationManager.AppSettings["SYMBOLS_LENGTH_DEFAULT"]);
        public static int PAGE_SIZE = int.Parse(ConfigurationManager.AppSettings["PAGE_SIZE"]);

        public static string NOOF_ENTRIES_PER_INTERNAL_NODE = "NOOF_ENTRIES_PER_INTERNAL_NODE";
        public static string NOOF_ENTRIES_PER_LEAF_NODE = "NOOF_ENTRIES_PER_LEAF_NODE";

        public static int DISTANCE_CALC = 0;

        public static int SEARCH_TIME_RTREE = 0;
        public static int SEARCH_TIME_TSTREE = 0;

        public static int SEARCH_CANDIDATE_RTREE = 0;
        public static int SEARCH_CANDIDATE_TSTREE = 0;

       

        public const float MAXREAL = (float)9.99e20;
        public static double EuclideDistance(double[] v1, double[] v2)
        {
            ++DISTANCE_CALC;

            double result = 0;
            int n = v1.Length;
            int i;
            for (i = 0; i < n; ++i)
            {
                result += Math.Pow(v1[i] - v2[i], 2);
            }

            return Math.Sqrt(result);
        }

        public static float EuclideDistance(float[] v1, float[] v2)
        {
            ++DISTANCE_CALC;

            double result = 0;
            int n = v1.Length;
            int i;
            for (i = 0; i < n; ++i)
            {
                result += Math.Pow(v1[i] - v2[i], 2);
            }

            return (float)Math.Sqrt(result);
        }

        public static double EuclideDistance(float[] v1, double[] v2)
        {
            ++DISTANCE_CALC;

            double result = 0;
            int n = v1.Length;
            int i;
            for (i = 0; i < n; ++i)
            {
                result += Math.Pow(v1[i] - v2[i], 2);
            }

            return (float)Math.Sqrt(result);
        }

        public static double EuclideDistance(double[] v1, float[] v2)
        {
            ++DISTANCE_CALC;

            double result = 0;
            int n = v1.Length;
            int i;
            for (i = 0; i < n; ++i)
            {
                result += Math.Pow(v1[i] - v2[i], 2);
            }

            return (float)Math.Sqrt(result);
        }

        public static double w_dist(double current_dist, int p1_index, int p2_index, double[] p1, float[] p2)
        {
            int length = p1.Length;
            double d1, d2, d3;

            if (p1_index + 1 >= length || p2_index + 1 >= length) d1 = MAXREAL;
            else d1 = Math.Pow(p1[p1_index + 1] - p2[p2_index + 1], 2);

            if (p1_index >= length || p2_index + 1 >= length) d2 = MAXREAL;
            else d2 = Math.Pow(p1[p1_index] - p2[p2_index + 1], 2);

            if (p1_index + 1 >= length || p2_index >= length) d3 = MAXREAL;
            else d3 = Math.Pow(p1[p1_index + 1] - p2[p2_index], 2);

            if (p1_index == length && p2_index == length) return current_dist;
            else
            {
                if (d1 == MAXREAL)
                {
                    if (d2 == MAXREAL)
                    {

                        if (d3 != MAXREAL) return w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2);
                        else
                        {
                            return current_dist;
                        }
                    }
                    else
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2));
                        }
                        else return w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2);
                    }
                }
                else
                {
                    if (d2 == MAXREAL)
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2));
                        }
                        else return w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2);
                    }
                    else
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), Math.Min(w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2)));
                        }
                        else
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2));
                        }
                    }
                }
            }
          
        }

        public static double w_dist(double current_dist, int p1_index, int p2_index, double[] p1, double[] p2)
        {
            int length = p1.Length;
            double d1, d2, d3;

            if (p1_index + 1 >= length || p2_index + 1 >= length) d1 = MAXREAL;
            else d1 = Math.Pow(p1[p1_index + 1] - p2[p2_index + 1], 2);

            if (p1_index >= length || p2_index + 1 >= length) d2 = MAXREAL;
            else d2 = Math.Pow(p1[p1_index] - p2[p2_index + 1], 2);

            if (p1_index + 1 >= length || p2_index >= length) d3 = MAXREAL;
            else d3 = Math.Pow(p1[p1_index + 1] - p2[p2_index], 2);

            if (p1_index == length && p2_index == length) return current_dist;
            else
            {
                if (d1 == MAXREAL)
                {
                    if (d2 == MAXREAL)
                    {

                        if (d3 != MAXREAL) return w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2);
                        else
                        {
                            return current_dist;
                        }
                    }
                    else
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2));
                        }
                        else return w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2);
                    }
                }
                else
                {
                    if (d2 == MAXREAL)
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2));
                        }
                        else return w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2);
                    }
                    else
                    {
                        if (d3 != MAXREAL)
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), Math.Min(w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2), w_dist(current_dist + d3, p1_index + 1, p2_index, p1, p2)));
                        }
                        else
                        {
                            return Math.Min(w_dist(current_dist + d1, p1_index + 1, p2_index + 1, p1, p2), w_dist(current_dist + d2, p1_index, p2_index + 1, p1, p2));
                        }
                    }
                }
            }

        }
        public static double DTWdistance( float[] p1, double[] p2)
        {
         
            DTWTransform transform = new DTWTransform();
            double[] lower;
            double[] upper;

            int rRatio = 3;
            transform.Transform(rRatio, p2);

            lower = transform.d_lower;
            upper = transform.d_upper;

            float f = 0;
            int length = p1.Length;

            for (int i = 0; i < length; i++)
            {
                if (p1[i] < lower[i])
                {
                    // < lower bounces
                    f += (float)Math.Pow(p1[i] - lower[i], 2);
                }
                else if (p1[i] > upper[i])
                {
                    //> upper bounce
                    f += (float)Math.Pow(p1[i] - upper[i], 2);
                }
                else
                {
                    //0
                    f += 0;
                }
            }
            return Math.Sqrt(f);

        }

        public static double DTWdistance(double[] p1, double[] p2)
        {
        
            DTWTransform transform = new DTWTransform();
            double[] lower;
            double[] upper;

            int rRatio = 3;
            transform.Transform(rRatio, p2);

            lower = transform.d_lower;
            upper = transform.d_upper;

            float f = 0;
            int length = p1.Length;

            for (int i = 0; i < length; i++)
            {
                if (p1[i] < lower[i])
                {
                    // < lower bounces
                    f += (float)Math.Pow(p1[i] - lower[i], 2);
                }
                else if (p1[i] > upper[i])
                {
                    //> upper bounce
                    f += (float)Math.Pow(p1[i] - upper[i], 2);
                }
                else
                {
                    //0
                    f += 0;
                }
            }
            return Math.Sqrt(f);


        }
        public static double[] setGaussianTable(int length)
        {
            double[] d;
            d = new double[length - 1];
            for (int i = 0; i < length - 1; i++)
            {
                Normal normalDist = new Normal();
                d[i] = normalDist.InverseCumulativeDistribution((float)(i + 1) / length);
            }
            return d;
        }

        private static int GetIndexInAlphabet(char value)
        {
            // Uses the uppercase character unicode code point. 'A' = U+0042 = 65, 'Z' = U+005A = 90
            char upper = char.ToUpper(value);
            if (upper < 'A' || upper > 'Z')
            {

                throw new ArgumentOutOfRangeException("value", upper + "-This method only accepts standard Latin characters.");
            }

            return (int)upper - (int)'A';
        }

        public static int GetIndexInAlphabet(char value, string ex)
        {
            // Uses the uppercase character unicode code point. 'A' = U+0042 = 65, 'Z' = U+005A = 90
            char upper = value; // char.ToUpper(value);
            if ((int)upper < 33 || (int)upper > 126)
            {
                throw new ArgumentOutOfRangeException("This method only accepts standard Latin characters.");
            }

            return (int)upper - (int)'!';
        }

        public static double SaxDistance(char[] c1, char[] c2, int length)
        {
            double t = 0;
            double[] temp;
            temp = new double[length - 1];

            if (c1.Length != c2.Length)
            {
                Console.WriteLine("Length is not the same !");
            }

            Array.Copy(setGaussianTable(length), temp, length - 1);

            for (int i = 0; i < c1.Length; i++)
            {
                int c1Index = GetIndexInAlphabet(c1[i]);
                int c2Index = GetIndexInAlphabet(c2[i]);

                if (Math.Abs((c1Index + 1) - (c2Index + 1)) <= 1)
                {
                    t += 0;
                }
                else if ((c1Index + 1) < (c2Index + 1) - 1)
                {
                    t += Math.Pow(temp[c2Index - 1] - temp[c1Index], 2);
                }
                else if ((c1Index + 1) > (c2Index + 1) - 1)
                {
                    t += Math.Pow(temp[c1Index - 1] - temp[c2Index], 2);
                }
                Console.WriteLine(t.ToString());
            }
            return Math.Sqrt(t);

        }

       
    }
}
